package moonstar.Calculator;


/*
* 传入一串字符串，首先得确保合理。如果不合理就是错误的答案，概不提醒。
*
* 如果用栈去实现计算器的功能。
*
* 1，首先需要准备两个栈，一个是符号栈，一个是数栈
* 2，遍历整个字符串，这里需要做一个确保隔着数字的是一个数
*   如果遇到右括号，则依次弹符号栈符号和数栈进行操作，直到遇到第一个左括号。然后继续遍历。
    *   2、1、如果是数字，需要先判断其下一个是否也是数字，如果是数字需要做相加之后继续1的判断然后入数栈
    *   2、2、如果是符号，判断符号栈是否空。
    *       2、2、1如果是空栈，直接入符号栈。
    *       2、2、2如果非空，判断该符号的优先级是否完全大于顶栈符号的优先级，用order。order越小优先级越大
    *          2、2、2、1如果完全大于，则直接入符号栈
    *          2、2、2、2反之，弹出符号栈和数栈两个元素进行对应操作
    如果遇到左括号，则直接入栈。
* 3、遍历完整个字符串之后，依次弹出符号栈元素，和数栈元素进行操作。 如果有错误，则输入有错误。
*
*
* */

import moonstar.Calculator.OperationImpl.add;
import moonstar.Calculator.OperationImpl.division;
import moonstar.Calculator.OperationImpl.multiply;
import moonstar.Calculator.OperationImpl.subtract;

import java.util.ArrayList;
import java.util.Stack;

public class Calculate {

    public static ArrayList<Operation> operations = new ArrayList<>();

    static {
        operations.add(new add());
        operations.add(new division());
        operations.add(new subtract());
        operations.add(new multiply());
    }


    /**
     * 根据穿过来的操作字符，匹配对应的operation实体类并且返回
     *
     * @param op
     * @return
     */
    public static Operation matchOperation(char op) {
        for (Operation operation : operations) {
            if (operation.getchar() == op) {
                return operation;
            }
        }
        throw new RuntimeException("暂时没有对应的操作符，或者传入的符号有错误！！！");
    }

    public static boolean isGreater(Operation o1, Operation o2) {
        return o1.getOrder() < o2.getOrder();
    }

    public static double calculate(String str) {
        Stack<Double> vals = new Stack<>();
        Stack<Character> characters = new Stack<>();
        //每个字符都对应某一个operation实体类，其操作方法就是operation的calculate方法
        for (int i = 0; i < str.length(); i++) {
            //如果遇到了右括号，则直接弹两个栈并且进行操作
            char c = str.charAt(i);
            if (c == ')') {
                while (characters.peek() != '(') {
                    //只要栈顶不是（，就弹进行操作
                    Character pop = characters.pop();
                    Operation operation = matchOperation(pop);
                    Double pop1 = vals.pop();
                    Double pop2 = vals.pop();
                    double calculate = operation.calculate(pop2, pop1);
                    //将计算的结果入数栈
                    vals.add(calculate);
                }
                //如果顶部是左括号，则将第一个左括号出栈
                characters.pop();
            }
            //如果是数字，相邻的数字是同一个数字放入数栈中
            else if (c <= '9' && c >= '0') {
                double val = c - '0';
                for (int j = i + 1; j < str.length(); j++) {
                    char c2 = str.charAt(j);
                    if (c2 <= '9' && c2 >= '0') {
                        double next = c2 - '0';
                        val = val  * 10 + next;
                        //给i重定位到j，因为j全部遍历完了
                        i = j;
                    } else {
                        break;
                    }
                }
                //此时i的位置已经全部处理完了,判断小数点部分
                if (i + 1 < str.length() && str.charAt(i + 1) == '.') {
                    int tool=i+1;
                    for (int j = i + 2; j < str.length(); j++) {
                        //TODO 第一个j如果不是数字那就肯定是错误的表达式，但是我们不处理这个点
                        char c3 = str.charAt(j);
                        if (c3 <= '9' && c3 >= '0') {
                            double next = c3 - '0';
                            val = val + next * Math.pow(0.1, j -tool);
                            //给i重定位到j，因为j全部遍历完了
                            i = j;
                        } else {
                            break;
                        }
                    }
                }
                vals.add(val);
            } else if (c == '(') {
                characters.add(c);
            } else {
                //最后一种可能就只有可能是符号了
                if (characters.isEmpty()) {
                    characters.add(c);
                } else {
                    Operation o1 = matchOperation(c);
                    //上一个符号不是左括号，并且本优先级高于前一个操作符。
                    if (characters.peek() != '(' && isGreater(o1, matchOperation(characters.peek()))) {
                        characters.add(c);
                    } else if (characters.peek() == '(') {
                        characters.add(c);
                    } else {
                        // 就弹进行操作
                        Character pop = characters.pop();
                        Operation operation = matchOperation(pop);
                        Double pop1 = vals.pop();
                        Double pop2 = vals.pop();
                        double calculate = operation.calculate(pop2, pop1);
                        //将计算的结果入数栈
                        vals.add(calculate);
                        //将本操作符号入栈。
                        characters.add(c);
                    }
                }
            }
        }
        //遍历完之后依次弹出符号栈元素，进行计算。
        while (!characters.isEmpty()) {
            // 就弹进行操作
            Character pop = characters.pop();
            Operation operation = matchOperation(pop);
            Double pop1 = vals.pop();
            Double pop2 = vals.pop();
            double calculate = operation.calculate(pop2, pop1);
            //将计算的结果入数栈
            vals.add(calculate);
        }

        return vals.pop();
    }
}

